package com.googlecode.antilopebpm.variable.converter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.antilopebpm.exception.AntilopeRuntimeException;
import com.googlecode.antilopebpm.variable.VariableConverter;
import com.googlecode.antilopebpm.variable.instance.reference.ByteArray;

public class SerializableToByteArrayConverter implements VariableConverter {
	private static final Logger logger = LoggerFactory
			.getLogger(SerializableToByteArrayConverter.class);

	private static final long serialVersionUID = 1L;

	public boolean supports(Object value) {
		if (value == null)
			return true;
		return Serializable.class.isAssignableFrom(value.getClass());
	}

	public ByteArray convert(Object o) {
		byte[] bytes = null;
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(o);
			oos.flush();
			bytes = baos.toByteArray();
		} catch (IOException e) {
			throw new AntilopeRuntimeException(
					"couldn't serialize '" + o + "'", e);
		}

		return new ByteArray(bytes);
	}

	public Object revert(Object o) {
		ByteArray byteArray = (ByteArray) o;
		try {
			ByteArrayInputStream bais = new ByteArrayInputStream(byteArray
					.getBytes());
			CustomObjectInputStream ois = new CustomObjectInputStream(bais);
			return ois.readObject();
		} catch (Exception e) {
			throw new AntilopeRuntimeException("couldn't deserialize object", e);
		}
	}

	/**
	 * Custom ObjectInputStream implementation to more appropriately handle
	 * classloading within app servers.
	 */
	private static final class CustomObjectInputStream extends
			ObjectInputStream {

		public CustomObjectInputStream(InputStream in) throws IOException {
			super(in);
		}

		protected Class resolveClass(ObjectStreamClass v) throws IOException,
				ClassNotFoundException {
			String className = v.getName();
			Class resolvedClass = null;

			logger.trace("Attempting to locate class [" + className + "]");

			ClassLoader loader = Thread.currentThread().getContextClassLoader();
			try {
				resolvedClass = loader.loadClass(className);
				logger.trace("Class resolved through context class loader");
			} catch (ClassNotFoundException e) {
				logger.trace("Asking super to resolve");
				resolvedClass = super.resolveClass(v);
			}

			return resolvedClass;
		}
	}
}
